#include "stdafx.h"
#include "CameraFrame.h"
#include "dlgnodelist.h"
#include "dlgcamparms.h"
#include "dlgusersize.h"
#include "dlgdirectaccess.h"
#include "dlgabout.h"
#include "dlgfeature.h"
#include "grabdemo.h"
#include ".\cameraframe.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

char* InfoStr[] =
{
  "No license available.",
  "GUID from 1304 card.",
  "Harddisk license.",
  "Ethernet-MAC license.",
  "Vendor specific license.",
  "Date limited license.",
  "Device specific license.",
  "Frame limited license."  
};

/////////////////////////////////////////////////////////////////////////////
// Constructor for our thread.

CCameraThread::CCameraThread(CCameraFrame *pCameraFrame,int Left,int Top)
{
  m_pCameraFrame=pCameraFrame;
  m_Left=Left;
  m_Top=Top;
  m_bAutoDelete=TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// This is our thread init routine.

BOOL CCameraThread::InitInstance()
{
  RECT FrameRect;

  // Create our window
  if(!m_pCameraFrame->Create(((CGrabDemoApp*)AfxGetApp())->m_pFrameClassName,
             "CameraFrame",
             WS_OVERLAPPEDWINDOW,
             CFrameWnd::rectDefault,
             NULL,
             MAKEINTRESOURCE(IDM_CAMFRAME),
             0,
             NULL))
  {
    // Notify caller
    SetEvent(m_pCameraFrame->m_hEvent);
    
    // Set pointer to null so we don't delete this object on ExitInstance
    m_pCameraFrame=NULL;
    return FALSE;
  }

  // Move to appropriate position
  m_pCameraFrame->GetWindowRect(&FrameRect);
  m_pCameraFrame->MoveWindow(m_Left,m_Top,
               FrameRect.right-FrameRect.left,FrameRect.bottom-FrameRect.top);

  // Recalculate layout including status bar
  m_pCameraFrame->RecalcLayout();
  
  // Set new size
  m_pCameraFrame->SetViewSize(640,480);
  
  // Show window and draw
  m_pCameraFrame->ShowWindow(TRUE);
  m_pCameraFrame->UpdateWindow();

  // Set result to TRUE and notify caller
  m_pCameraFrame->m_InitOk=TRUE;
  SetEvent(m_pCameraFrame->m_hEvent);

  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// This is our thread exit routine.

int CCameraThread::ExitInstance()
{
  // Delete our frame
  if(m_pCameraFrame)
  {
    delete m_pCameraFrame;
    m_pCameraFrame=NULL;
  }

  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CCameraFrame

CCameraFrame::CCameraFrame(CAMERAINFO *pCameraInfo)
{
  m_SettingsValid=FALSE;
  m_pDlgModal=NULL;
  m_CameraActive=FALSE;
  processingMode=FALSE;
  m_pCameraInfo=pCameraInfo;
  memset(&m_CamParms,0,sizeof(m_CamParms));
  m_hEvent=::CreateEvent(NULL,FALSE,FALSE,NULL);
  m_MemorySize=0;
  m_pStatusBar=NULL;
}

/////////////////////////////////////////////////////////////////////////////
// Destructor.

CCameraFrame::~CCameraFrame()
{
  if(m_pStatusBar)
   delete m_pStatusBar;

  if(m_hEvent)
   CloseHandle(m_hEvent);
}

BEGIN_MESSAGE_MAP(CCameraFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CCameraFrame)
	ON_WM_CREATE()
	ON_WM_CLOSE()
	ON_COMMAND(IDI_EDITSETTINGS, OnEditsettings)
	ON_COMMAND(IDI_RUN, OnRun)
	ON_COMMAND(IDI_PROCESSROBOTS, ProcessRobots)
	ON_COMMAND(IDI_160X120, On160x120)
	ON_COMMAND(IDI_320X240, On320x240)
	ON_COMMAND(IDI_1024X768, On1024x768)
	ON_COMMAND(IDI_1280X960, On1280x960)
	ON_COMMAND(IDI_640X480, On640x480)
	ON_COMMAND(IDI_800X600, On800x600)
	ON_COMMAND(IDI_FEATURE, OnFeature)
	ON_COMMAND(IDI_1600X1200, On1600x1200)
	ON_COMMAND(IDI_USERSIZE, OnUsersize)
	ON_COMMAND(IDI_SAVE, OnSave)
	ON_COMMAND(IDI_SCROLLMODE, OnScrollmode)
	ON_COMMAND(IDI_DIRECTACCESS, OnDirectAccess)
	ON_COMMAND(IDI_ABOUT, OnAbout)
	ON_MESSAGE(WM_FGNOTIFY,OnFgNotify)
	ON_MESSAGE(WM_SELECTION,OnSelection)
	//}}AFX_MSG_MAP
        ON_COMMAND(ID_OTHERS_LICENSEREQUEST, OnOthersLicenseRequest)
        ON_COMMAND(ID_OTHERS_LICENSEINFO, OnOthersLicenseInfo)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Collect camera parameters we are interested in.

BOOL CCameraFrame::GetCameraParms(CAMERAPARMS *pCamParms)
{
  UINT32  Result;
  UINT32  Value;
  FGPINFO Info;

  Result=m_Camera.GetParameter(FGP_IMAGEFORMAT,&Value);
  pDlgNodeList->CheckResult("GetParameter(FGP_IMAGEFORMAT)",Result);
  if(Result==FCE_NOERROR)
  {
    pCamParms->ImageFormat=Value;
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameterInfo(FGP_XSIZE,&Info);
    pDlgNodeList->CheckResult("GetParameterInfo(FGP_XSIZE)",Result);
    if(Result==FCE_NOERROR)
    {
      pCamParms->XSize=(UINT16)Info.IsValue;;
      pCamParms->XSizeMax=(UINT16)Info.MaxValue;;
      pCamParms->XSizeUnit=(UINT16)Info.Unit;;
    }  
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameterInfo(FGP_YSIZE,&Info);
    pDlgNodeList->CheckResult("GetParameterInfo(FGP_YSIZE)",Result);
    if(Result==FCE_NOERROR)
    {
      pCamParms->YSize=(UINT16)Info.IsValue;;
      pCamParms->YSizeMax=(UINT16)Info.MaxValue;;
      pCamParms->YSizeUnit=(UINT16)Info.Unit;;
    }  
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameterInfo(FGP_XPOSITION,&Info);
    pDlgNodeList->CheckResult("GetParameterInfo(FGP_XPOSITION)",Result);
    if(Result==FCE_NOERROR)
    {
      pCamParms->XPos=(UINT16)Info.IsValue;;
      pCamParms->XPosMax=(UINT16)Info.MaxValue;;
      pCamParms->XPosUnit=(UINT16)Info.Unit;;
    }  
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameterInfo(FGP_YPOSITION,&Info);
    pDlgNodeList->CheckResult("GetParameterInfo(FGP_YPOSITION)",Result);
    if(Result==FCE_NOERROR)
    {
      pCamParms->YPos=(UINT16)Info.IsValue;;
      pCamParms->YPosMax=(UINT16)Info.MaxValue;;
      pCamParms->YPosUnit=(UINT16)Info.Unit;;
    }  
  }

  if(Result==FCE_NOERROR && IMGRES(pCamParms->ImageFormat)==RES_SCALABLE)
  {
    Result=m_Camera.GetParameter(FGP_PACKETSIZE,&Value);
    pDlgNodeList->CheckResult("GetParameter(FGP_PACKETSIZE)",Result);
    if(Result==FCE_NOERROR)
     pCamParms->PktSize=(UINT16)Value;
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameter(FGP_PHYSPEED,&Value);
    pDlgNodeList->CheckResult("GetParameter(FGP_PHYSPEED)",Result);
    if(Result==FCE_NOERROR)
     pCamParms->PhySpeed=(UINT8)Value;
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameter(FGP_TRIGGER,&Value);
    if(Result==FCE_NOTAVAILABLE)
    {
      pCamParms->Trigger=0;
      Result=FCE_NOERROR;
    }
    else
    {
      pDlgNodeList->CheckResult("GetParameter(FGP_TRIGGER)",Result);
      if(Result==FCE_NOERROR)
       pCamParms->Trigger=Value;
    }
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameter(FGP_FRAMEBUFFERCOUNT,&Value);
    pDlgNodeList->CheckResult("GetParameter(FGP_FRAMEBUFFERCOUNT)",Result);
    if(Result==FCE_NOERROR)
     pCamParms->FrameBufferCount=Value;
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.GetParameter(FGP_DMAFLAGS,&Value);
    pDlgNodeList->CheckResult("GetParameter(FGP_DMAFLAGS)",Result);
    if(Result==FCE_NOERROR)
     pCamParms->DmaFlags=Value;
  }

  pCamParms->IsResized=FALSE;
  
  if(Result!=FCE_NOERROR)
   return FALSE;

  UpdateRectangleInfo();
 
  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Set camera parameters.

BOOL CCameraFrame::SetCameraParms(CAMERAPARMS *pCamParms)
{
  UINT32 Result;

  Result=m_Camera.SetParameter(FGP_IMAGEFORMAT,pCamParms->ImageFormat);
  pDlgNodeList->CheckResult("SetParameter(FGP_IMAGEFORMAT)",Result);

  if(IMGRES(pCamParms->ImageFormat)==RES_SCALABLE)
  {
    if(Result==FCE_NOERROR)
    {
      Result=m_Camera.SetParameter(FGP_XSIZE,pCamParms->XSize);
      pDlgNodeList->CheckResult("SetParameter(FGP_XSIZE)",Result);
    }

    if(Result==FCE_NOERROR)
    {
      Result=m_Camera.SetParameter(FGP_YSIZE,pCamParms->YSize);
      pDlgNodeList->CheckResult("SetParameter(FGP_YSIZE)",Result);
    }

    if(Result==FCE_NOERROR)
    {
      Result=m_Camera.SetParameter(FGP_XPOSITION,pCamParms->XPos);
      pDlgNodeList->CheckResult("SetParameter(FGP_XPOSITION)",Result);
    }

    if(Result==FCE_NOERROR)
    {
      Result=m_Camera.SetParameter(FGP_YPOSITION,pCamParms->YPos);
      pDlgNodeList->CheckResult("SetParameter(FGP_YPOSITION)",Result);
    }

    if(Result==FCE_NOERROR)
    {
      Result=m_Camera.SetParameter(FGP_PACKETSIZE,pCamParms->PktSize);
      pDlgNodeList->CheckResult("SetParameter(FGP_PACKETSIZE)",Result);
    }
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.SetParameter(FGP_PHYSPEED,pCamParms->PhySpeed);
    pDlgNodeList->CheckResult("SetParameter(FGP_PHYSPEED)",Result);
  }

  if(Result==FCE_NOERROR)
  {
    // Don't log result - some cameras don't have trigger
    m_Camera.SetParameter(FGP_TRIGGER,pCamParms->Trigger);
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.SetParameter(FGP_FRAMEBUFFERCOUNT,pCamParms->FrameBufferCount);
    pDlgNodeList->CheckResult("SetParameter(FGP_FRAMEBUFFERCOUNT)",Result);
  }

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.SetParameter(FGP_DMAFLAGS,pCamParms->DmaFlags);
    pDlgNodeList->CheckResult("SetParameter(FGP_DMAFLAGS)",Result);
  }

  pCamParms->IsResized=FALSE;
  
  if(Result!=FCE_NOERROR)
   return FALSE;

  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Reaction on window create.

int CCameraFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  char             Title[128];
  CCreateContext   Context;
  UINT32           Result,XSize,FrameSize;
  CRuntimeClass   *pRTC = RUNTIME_CLASS(CCameraView);
  UINT             StatusIds[] =
                   { IDS_SEEN,IDS_PANEDEF,
                     IDS_SKIP,IDS_PANEDEF,
                     IDS_RATE,IDS_RATEDEF,
                     IDS_MODE,IDS_PANEDEF
                   };

  // Create window and get window handle
  if(CFrameWnd::OnCreate(lpCreateStruct) == -1)
   return -1;

  // Set connection speed if limited
  if(m_pCameraInfo->LimitSpeed)
   m_Camera.SetParameter(FGP_PHYSPEED,PS_100MBIT);

  // Connect with our camera as early as possible
  Result=m_Camera.Connect(&m_pCameraInfo->Guid,(void*)m_hWnd);
  pDlgNodeList->CheckResult("Connect",Result);
  if(Result!=FCE_NOERROR)
   return -1;

  // Put our cameraview class into our context
  memset(&Context,0,sizeof(Context));
  Context.m_pNewViewClass=pRTC;

  // Create our CCameraView implicit and select as active one
  m_pView=(CCameraView*)CreateView(&Context);
  if(m_pView)
  {
    SetActiveView(m_pView);
    m_pView->EnableSelection(TRUE,m_hWnd,WM_SELECTION);
  }
  
  // Create Statusbar
  m_pStatusBar=new CStatusBar;
  m_pStatusBar->Create(this,WS_CHILD|WS_VISIBLE|CBRS_BOTTOM,0);

  // Set fields of our statusbar
  m_pStatusBar->SetIndicators(StatusIds,8);

  // Set style of our status panes
  m_pStatusBar->SetPaneStyle(0,SBPS_NOBORDERS);
  m_pStatusBar->SetPaneStyle(2,SBPS_NOBORDERS);
  m_pStatusBar->SetPaneStyle(4,SBPS_NOBORDERS);
  m_pStatusBar->SetPaneStyle(6,SBPS_NOBORDERS);

  memset(&m_CamParms,0,sizeof(m_CamParms));
  if(GetCameraParms(&m_CamParms))
  {
    if(IMGCOL(m_CamParms.ImageFormat)<128)
    {
      m_pView->CreateBitmap(m_CamParms.XSize,m_CamParms.YSize,
                            (UINT8)IMGCOL(m_CamParms.ImageFormat),
                            m_CamParms.BayerPattern,
                            m_CamParms.YIsRawMode,
                            m_CamParms.Y16Invert);
    }
    else
    {
      Result=m_Camera.GetParameter(FGP_FRAMEMEMORYSIZE,&FrameSize);
      if(Result==FCE_NOERROR)
      {
        XSize=FrameSize/m_CamParms.YSize;
         
        // We extend our XSize to reflect color format
        m_pView->CreateBitmap(XSize,m_CamParms.YSize,CM_RAW16,
                              m_CamParms.BayerPattern,
                              m_CamParms.YIsRawMode,
                              m_CamParms.Y16Invert);
      }
    }

    m_SettingsValid=TRUE;
  }

  // Set value of panes
  m_pStatusBar->SetPaneText(1,"0");
  m_pStatusBar->SetPaneText(3,"0");
  m_pStatusBar->SetPaneText(5,"0.0");
  m_pStatusBar->SetPaneStyle(7,SBPS_STRETCH);
  UpdateModePane();

  // Set our title
  m_Camera.GetDeviceName(Title,sizeof(Title));
  SetWindowText(Title);

  return 0;
}

/////////////////////////////////////////////////////////////////////////////
// Set frame window size so that we get an exact view size.

void CCameraFrame::SetViewSize(UINT32 Width,UINT32 Height)
{
  RECT FrameRect,ViewRect;
  int  AddWidth,AddHeight;

  GetWindowRect(&FrameRect);
  m_pView->GetWindowRect(&ViewRect);

  AddWidth=(FrameRect.right-FrameRect.left)-(ViewRect.right-ViewRect.left)+
            2*GetSystemMetrics(SM_CXEDGE);
  AddHeight=FrameRect.bottom-FrameRect.top-(ViewRect.bottom-ViewRect.top)+
            2*GetSystemMetrics(SM_CYEDGE);

  MoveWindow(FrameRect.left,FrameRect.top,Width+AddWidth,Height+AddHeight);
}

/////////////////////////////////////////////////////////////////////////////
// Handle close message.

void CCameraFrame::OnClose()
{
  // If active modal dialog we must first close this one to return from MsgLoop
  if(m_pDlgModal!=NULL)
  {
    m_pDlgModal->EndDialog(IDCANCEL);
    m_pDlgModal=NULL;
  }

  // Disconnect from camera
  m_Camera.Disconnect();

  // Remove ourself from node list
  if(m_pCameraInfo)
   pDlgNodeList->OnFrameClosing(m_pCameraInfo);

  // Report memory usage
  if(m_MemorySize)
  {
    pDlgNodeList->ReportMemoryUsage(-m_MemorySize);
    m_MemorySize=0;
  }
  
  // Post quit message to our thread (he is the one who deletes us)
  PostMessage(WM_QUIT);

  // Do base class close
  CFrameWnd::OnClose();
}

/////////////////////////////////////////////////////////////////////////////
// Update mode pane in status bar.

void CCameraFrame::UpdateModePane()
{
  char cbuf[128];

  if(!m_SettingsValid)
  {
    m_pStatusBar->SetPaneText(7,"Unknown");
  }
  else
  {
    if(IMGRES(m_CamParms.ImageFormat)!=RES_SCALABLE)
    {
      wsprintf(cbuf,"%dx%d %s %s f/s",
               m_CamParms.XSize,m_CamParms.YSize,
               FGColorFormatStr[IMGCOL(m_CamParms.ImageFormat)],
               FGFrameRateStr[IMGRATE(m_CamParms.ImageFormat)]);
    }
    else
    {
      if(IMGCOL(m_CamParms.ImageFormat)<CM_LAST)
       wsprintf(cbuf,"%dx%d %s",m_CamParms.XSize,m_CamParms.YSize,
                FGColorFormatStr[IMGCOL(m_CamParms.ImageFormat)]);
      else
       wsprintf(cbuf,"%dx%d Id=%d",m_CamParms.XSize,m_CamParms.YSize,
                IMGCOL(m_CamParms.ImageFormat));
    }

    m_pStatusBar->SetPaneText(7,cbuf);
  }
}

/////////////////////////////////////////////////////////////////////////////
// Update all rectangles.

void CCameraFrame::UpdateRectangleInfo()
{
  m_SensorRect.left=m_SensorRect.top=0;
  m_SensorRect.right=m_CamParms.XSizeMax;
  m_SensorRect.bottom=m_CamParms.YSizeMax;
  
  m_CameraRect.left=m_CamParms.XPos;
  m_CameraRect.top=m_CamParms.YPos;
  m_CameraRect.right=m_CameraRect.left+m_CamParms.XSize;
  m_CameraRect.bottom=m_CameraRect.top+m_CamParms.YSize;
  
  m_OrgRect=m_CameraRect;
  
  m_ViewRect.left=m_ViewRect.top=0;
  m_ViewRect.right=m_CamParms.XSize;
  m_ViewRect.bottom=m_CamParms.YSize;
}

/////////////////////////////////////////////////////////////////////////////
// Handle Edit settings message.

void CCameraFrame::OnEditsettings()
{
  CAMERAPARMS   EditParms;
  CDlgCamParms  Dlg(&m_Camera,&EditParms,this);
  BOOL          MustStart=FALSE;
  UINT32        FrameSize,XSize,Result;

  // Always reset our zoom settings
  OnSelection(0,0);
  
  // Stop camera before we change settings
  if(m_CameraActive)
  {     
    OnRun();
    MustStart=TRUE;
  }

  // Copy parameters
  EditParms=m_CamParms;

  // Start dialog
  if(Dlg.DoModal()==IDOK)
  {
    m_CamParms=EditParms;
    if(SetCameraParms(&m_CamParms))
    {
      // Always read back
      GetCameraParms(&m_CamParms);
      
      // Change view bitmap
      if(IMGCOL(m_CamParms.ImageFormat)<128)
      {
        m_pView->CreateBitmap(m_CamParms.XSize,m_CamParms.YSize,
                              (UINT8)IMGCOL(m_CamParms.ImageFormat),
                              m_CamParms.BayerPattern,
                              m_CamParms.YIsRawMode,
                              m_CamParms.Y16Invert);
      }
      else
      {
        Result=m_Camera.GetParameter(FGP_FRAMEMEMORYSIZE,&FrameSize);
        if(Result==FCE_NOERROR)
        {
          XSize=FrameSize/m_CamParms.YSize;
           
          // We extend our XSize to reflect color format
          m_pView->CreateBitmap(XSize,m_CamParms.YSize,CM_RAW8,
                                m_CamParms.BayerPattern,
                                m_CamParms.YIsRawMode,
                                m_CamParms.Y16Invert);
        }
      }

      // Show new settings in pane
      UpdateModePane();
    }
  }
  else
  {
    // Reset camera parameters
    SetCameraParms(&m_CamParms);
  }

  m_pDlgModal=NULL;

  if(MustStart)
   OnRun();
}


// start and stop processing
void CCameraFrame::ProcessRobots()
{
	if( !processingMode ){
		processingMode = TRUE;
		GetMenu()->CheckMenuItem(IDI_PROCESSROBOTS,MF_CHECKED);
		if(!m_CameraActive)
			OnRun();
		m_pView->recal = TRUE;
		m_pView->colorSelectStage = 0;
		m_pView->processImage = TRUE;
	}
	else{
		processingMode = FALSE;
		GetMenu()->CheckMenuItem(IDI_PROCESSROBOTS,MF_UNCHECKED);
		m_pView->processImage = FALSE;
	}
}

/////////////////////////////////////////////////////////////////////////////
// Start/stop acquisition.

void CCameraFrame::OnRun()
{
  char      Msg[64];
  UINT32    Result;
  UINT32    BufferCnt,RealBufferCnt;
  UINT32    FrameSize;

  if(!m_SettingsValid)
   return;

  // Check for XSize alignment
  if(m_CamParms.XSize%4)
  {
    MessageBox("Please change image X size to a multiple of 4!","Error",MB_OK);
    return;
  }

  if(!m_CameraActive)
  {
    // Store start time to calculate framerate
    m_StartTime=GetTickCount();
    m_FrameCnt=0;
    m_DiscardedCnt=0;

    // Reset frame rate field
    m_pStatusBar->SetPaneText(5,"0.0");

    // Set total and discarded fields
    m_pStatusBar->SetPaneText(1,"0");
    m_pStatusBar->SetPaneText(3,"0");

    // Set DMA mode to replace so we don't halt our DMA
    Result=m_Camera.SetParameter(FGP_DMAMODE,DMA_REPLACE);
    pDlgNodeList->CheckResult("SetParameter(FGP_DMAMODE)",Result);
    
    // Get planned buffer count 
    Result=m_Camera.GetParameter(FGP_FRAMEBUFFERCOUNT,&BufferCnt);
    pDlgNodeList->CheckResult("GetParameter(FGP_FRAMEBUFFERCOUNT)",Result);

    // Probably advise camera to NOT use isochronous resource manager (for extended bandwidth only)
    //m_Camera.SetParameter(FGP_USEIRMFORBW,0);
    
    // Choose ISO security check here
    //Result=m_Camera.SetParameter(FGP_DMAFLAGS,DMAF_CHECKSYNC|DMAF_CHECKCYCLE|DMAF_CHECKLENGTH);
    //pDlgNodeList->CheckResult("SetParameter(FGP_DMAFLAGS)",Result);

    // Choose frame start events here
    //Result=m_Camera.SetParameter(FGP_DMAFLAGS,DMAF_FRAMESTART);

    // Open DMA channel
    Result=m_Camera.OpenCapture();
    if(Result!=FCE_NOERROR && Result==FCE_PARTIAL)
     Result=FCE_NOERROR;

    pDlgNodeList->CheckResult("OpenCapture",Result);

    // Log partial allocation error
    if(Result==FCE_NOERROR)
    {
      m_Camera.GetParameter(FGP_FRAMEBUFFERCOUNT,&RealBufferCnt);
      if(BufferCnt!=RealBufferCnt)
      {
        wsprintf(Msg,"W: Partial buffer allocation (%d from %d)",
                 RealBufferCnt,BufferCnt);
        pDlgNodeList->LogMessage(Msg);
      }
    }

    // Get actual number of used frame buffers here
    m_Camera.GetParameter(FGP_FRAMEBUFFERCOUNT,&m_MaxFramesToDisplay);

    // Start device
    if(Result==FCE_NOERROR)
    {
      Result=m_Camera.StartDevice();
      pDlgNodeList->CheckResult("StartDevice",Result);
    }

    if(Result==FCE_NOERROR)
    {
      GetMenu()->CheckMenuItem(IDI_RUN,MF_CHECKED);
      m_CameraActive=TRUE;

      // Get size of a frame and report all used memory
      m_Camera.GetParameter(FGP_FRAMEMEMORYSIZE,&FrameSize);
      m_MemorySize=RealBufferCnt*FrameSize;
      pDlgNodeList->ReportMemoryUsage(m_MemorySize);
    }
    else
    {
      // Always redo on error
      m_Camera.StopDevice();
      m_Camera.CloseCapture();
    }
  }
  else
  {
    Result=m_Camera.StopDevice();
    pDlgNodeList->CheckResult("StopDevice",Result);

    m_Camera.CloseCapture();
    pDlgNodeList->CheckResult("CloseCapture",Result);

    m_CameraActive=FALSE;

    GetMenu()->CheckMenuItem(IDI_RUN,MF_UNCHECKED);

    pDlgNodeList->ReportMemoryUsage(-m_MemorySize);
    m_MemorySize=0;
  }
}

/////////////////////////////////////////////////////////////////////////////
// Setting view size...

void CCameraFrame::On160x120()
{
  SetViewSize(160,120);
}

void CCameraFrame::On320x240()
{
  SetViewSize(320,240);
}

void CCameraFrame::On1024x768()
{
  SetViewSize(1024,768);
}

void CCameraFrame::On1280x960()
{
  SetViewSize(1280,960);
}

void CCameraFrame::On1600x1200()
{
  SetViewSize(1600,1200);
}

void CCameraFrame::On640x480()
{
  SetViewSize(640,480);
}

void CCameraFrame::On800x600()
{
  SetViewSize(800,600);
}

/////////////////////////////////////////////////////////////////////////////
// Reaction on a new incoming frame.
// We only get notified once. We MUST read all frames until we get the 
// information that we have read the last frame.

void CCameraFrame::OnNewFrame()
{
  char      cbuf[32];
  FGFRAME   Frame;
  UINT32    Result;
  double    RunTime;
  UINT32    FramesToDisplay;

  // If a message was pending when someone stopped the camera we don't process
  if(!m_CameraActive)
   return;

  FramesToDisplay=min(16,m_MaxFramesToDisplay);
  do
  {
    // Try to read a frame
    Result=m_Camera.GetFrame(&Frame,0);
    
    // Any error?
    if(Result!=FCE_NOERROR)
    { 
      // Log any error except HALER_NODATA
      if(Result!=HALER_NODATA)
       pDlgNodeList->CheckResult("GetFrame",Result);
      
      // Stop loop on error
      break;
    }

    // Only if <pData> is valid (ignore pure frame start events)
    if(Frame.pData)
    {
      if(FramesToDisplay)
      {
        // Count and process this frame
        m_FrameCnt++;
        m_pView->ProcessFrame(Frame.pData,Frame.Length);
        
        FramesToDisplay--;
      }
      else
      {  
        // Discard all additional frames (system is obviuously too slow)
        m_DiscardedCnt++;
      }

      // Always return frame to camera object
      pDlgNodeList->CheckResult("PutFrame",m_Camera.PutFrame(&Frame));
    }
    else
    {
      // Here you can handle frame start notification
      Beep(2000,1);
    }
    
  }while(!(Frame.Flags&FGF_LAST));

  // Update number of frames
  wsprintf(cbuf,"%d",m_FrameCnt);
  m_pStatusBar->SetPaneText(1,cbuf);
  wsprintf(cbuf,"%d",m_DiscardedCnt);
  m_pStatusBar->SetPaneText(3,cbuf);

  // Calculate frame rate every 10th frame
  if(m_FrameCnt%16==0)
  {
    // We accept overrun after 50 days
    RunTime=(double)(GetTickCount()-m_StartTime)/1000.0;
    if(RunTime>1.0)
    {
      sprintf(cbuf,"%.1lf",(double)(m_FrameCnt+m_DiscardedCnt)/RunTime);
      m_pStatusBar->SetPaneText(5,cbuf);
    }
  }
}

/////////////////////////////////////////////////////////////////////////////
// Frame grabber notification.

LRESULT CCameraFrame::OnFgNotify(WPARAM wParam,LPARAM lParam)
{
  // See what happened
  switch(wParam)
  {
    case WPARAM_FRAMESREADY:
     OnNewFrame();
     break;
  }

  return 0;
}

/////////////////////////////////////////////////////////////////////////////
// Reaction on feature control.

void CCameraFrame::OnFeature()
{
  CDlgFeature *pDlgFeature;

  pDlgFeature=new CDlgFeature(&m_Camera);
  if(pDlgFeature)
   pDlgFeature->Create(IDD_FEATURE,this);
}

/////////////////////////////////////////////////////////////////////////////
// Set window size to user defined size.

void CCameraFrame::OnUsersize()
{
  CDlgUserSize *pDlg;
  RECT     ViewRect;
  DWORD    XSize,YSize;

  GetActiveView()->GetWindowRect(&ViewRect);
  XSize=ViewRect.right-ViewRect.left-2*GetSystemMetrics(SM_CXEDGE);
  YSize=ViewRect.bottom-ViewRect.top-2*GetSystemMetrics(SM_CYEDGE);;

  m_pDlgModal=pDlg=new CDlgUserSize(this);

  // Show dialog and change view size on success
  if(pDlg->DoModal(&XSize,&YSize)==IDOK)
   SetViewSize(XSize,YSize);

  m_pDlgModal=NULL;
  delete pDlg;
}

/////////////////////////////////////////////////////////////////////////////
// Write a bitmap file coming from our View.

BOOL CCameraFrame::WriteBitmap(char *pFilename)
{
  BITMAPINFO  *pInfo;
  UINT8       *pImage;
  OFSTRUCT    Ofs;
  HFILE       hFile;
  DWORD       i,LineSize,Offs;
  BITMAPFILEHEADER fh;
  BITMAPINFOHEADER bmi;

  // Get image information from our connected view
  pImage=m_pView->GetImage();
  pInfo=m_pView->GetBitmapInfo();

  if(!pInfo||!pImage)
   return FALSE;

  hFile=OpenFile(pFilename,&Ofs,OF_CREATE);
  if(hFile==HFILE_ERROR)
   return FALSE;

  // Copy Bitmapinfoheader (set Width and Height absolute)
  bmi=pInfo->bmiHeader;
  bmi.biWidth=abs(pInfo->bmiHeader.biWidth);
  bmi.biHeight=abs(pInfo->bmiHeader.biHeight);

  // Build fileheader and write
  fh.bfType=BMTYPE;
  if(pInfo->bmiHeader.biBitCount==8)
  {
    fh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+
                 256*sizeof(RGBQUAD);
    fh.bfSize=fh.bfOffBits+pInfo->bmiHeader.biSizeImage;
    bmi.biClrUsed=256;
    bmi.biClrImportant=0;
  }
  else
  {
    fh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    fh.bfSize   =fh.bfOffBits+pInfo->bmiHeader.biSizeImage;
    bmi.biClrUsed=0;
    bmi.biClrImportant=0;
  }

  fh.bfReserved1=0;
  fh.bfReserved2=0;

  // Write fileheader
  _hwrite(hFile,(char*)&fh,sizeof(BITMAPFILEHEADER));

  // Write bitmapinfoheader
  _hwrite(hFile,(char*)&bmi,sizeof(BITMAPINFOHEADER));

  // Write Palette (only if grayscale)
  if(bmi.biClrUsed!=0)
   _hwrite(hFile,(char*)pInfo->bmiColors,pInfo->bmiHeader.biClrUsed*sizeof(RGBQUAD));

  // Write all lines (from bottom up)
  LineSize=bmi.biWidth*bmi.biBitCount/8;
  Offs=LineSize*(bmi.biHeight-1);
  for(i=0;i<(UINT32)bmi.biHeight;i++)
  {
    _hwrite(hFile,(char*)&pImage[Offs],LineSize);
    Offs-=LineSize;
  }

  _lclose(hFile);

  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Save bitmap from our View.

void CCameraFrame::OnSave()
{
  CFileDialog *pDlg;
  char         Filename[256]="Image.bmp";

  if(m_pView && m_pView->GetImage() && m_pView->GetBitmapInfo())
  {
    pDlg=new CFileDialog(FALSE,"bmp",Filename,
                         OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
                         "Bitmaps (*.bmp)|*.bmp|All files (*.*)|*.*||",
                         this);
    m_pDlgModal=pDlg;
    if(pDlg)
    {
      pDlg->m_ofn.lpstrFile=Filename;
      pDlg->m_ofn.nMaxFile=sizeof(Filename);
      if(pDlg->DoModal()==IDOK)
      {
        WriteBitmap(Filename);
      }
      delete pDlg;
    }

    // Special handling: CFileDialog does not close with EndDialog (s. OnEndThread)
    // Instead it quits with the PostQuitMessage. So if we are here and the pointer
    // is NULL we must post the quit message again.
    if(m_pDlgModal==NULL)
     PostQuitMessage(0);

    m_pDlgModal=NULL;
  }
}

/////////////////////////////////////////////////////////////////////////////
// Change scrollmode of our view.

void CCameraFrame::OnScrollmode()
{
  if(GetMenu()->GetMenuState(IDI_SCROLLMODE,MF_BYCOMMAND)&MF_CHECKED)
  {
    GetMenu()->CheckMenuItem(IDI_SCROLLMODE,MF_UNCHECKED);
    if(m_pView)
     m_pView->SetScrollmode(FALSE);
  }
  else
  {
    GetMenu()->CheckMenuItem(IDI_SCROLLMODE,MF_CHECKED);
    if(m_pView)
     m_pView->SetScrollmode(TRUE);
  }
}

/////////////////////////////////////////////////////////////////////////////
// Allows direct register access to a camera.

void CCameraFrame::OnDirectAccess()
{
  CDlgDirectAccess Dlg(&m_Camera);

  m_pDlgModal=&Dlg;
  Dlg.DoModal();
  m_pDlgModal=NULL;
}

/////////////////////////////////////////////////////////////////////////////
// Show about dialog.

void CCameraFrame::OnAbout()
{
  CDlgAbout Dlg;

  m_pDlgModal=&Dlg;
  Dlg.DoModal();
  m_pDlgModal=NULL;
}

/////////////////////////////////////////////////////////////////////////////
// Create frame window for a camera view.
// Every frame window gets its own thread.

BOOL CCameraFrame::Start(int Left,int Top)
{
  CCameraThread *pThread;

  // Thread is self deleting so we don't have to store
  pThread=new CCameraThread(this,Left,Top);
  if(!pThread)
   return FALSE;
  
  // Set default init result to FALSE
  m_InitOk=FALSE;
  
  // Start our thread
  pThread->CreateThread();
  
  // Wait for end of init
  WaitForSingleObject(m_hEvent,INFINITE);
  
  return m_InitOk;
}

/////////////////////////////////////////////////////////////////////////////
// External stop function.

void CCameraFrame::Stop()
{
  // No external link anymore!
  m_pCameraInfo=NULL;
  SendMessage(WM_CLOSE,0,0);
}

/////////////////////////////////////////////////////////////////////////////
// We try to resize the image in scalable mode.
// The specified rectangle is the new AOI within the camera rectangle.

BOOL CCameraFrame::DoResize(RECT *pRect)
{
  UINT32 Result,PktSize;
  
  if(IMGRES(m_CamParms.ImageFormat)!=RES_SCALABLE)
   return FALSE;

  if(!pRect)
  {
    if(!m_CamParms.IsResized)
     return TRUE;
     
    m_CameraRect=m_OrgRect;
    PktSize=m_CamParms.PktSize;
    m_CamParms.IsResized=FALSE;
  }
  else
  {
    // Calculate new camera rectangle
    m_CameraRect.left+=pRect->left;
    m_CameraRect.top+=pRect->top;

    // Adjust positions
    m_CameraRect.left=(m_CameraRect.left/m_CamParms.XPosUnit)*m_CamParms.XPosUnit;
    m_CameraRect.top =(m_CameraRect.top/m_CamParms.YPosUnit)*m_CamParms.YPosUnit;
    
    m_CameraRect.right=((pRect->right-pRect->left-1)/m_CamParms.XSizeUnit+1)*
                       m_CamParms.XSizeUnit+m_CameraRect.left;
    m_CameraRect.bottom=((pRect->bottom-pRect->top-1)/m_CamParms.YSizeUnit+1)*
                        m_CamParms.YSizeUnit+m_CameraRect.top;

    PktSize=0;
    m_CamParms.IsResized=TRUE;
  }

  // Now we resize
  Result=m_Camera.SetParameter(FGP_RESIZE,1);
    
  if(Result==FCE_NOERROR)
   Result=m_Camera.SetParameter(FGP_XSIZE,m_CameraRect.right-m_CameraRect.left);
     
  if(Result==FCE_NOERROR)
   Result=m_Camera.SetParameter(FGP_YSIZE,m_CameraRect.bottom-m_CameraRect.top);

  if(Result==FCE_NOERROR)
   Result=m_Camera.SetParameter(FGP_XPOSITION,m_CameraRect.left);
    
  if(Result==FCE_NOERROR)
   Result=m_Camera.SetParameter(FGP_YPOSITION,m_CameraRect.top);
     
  if(Result==FCE_NOERROR && PktSize)
   Result=m_Camera.SetParameter(FGP_PACKETSIZE,PktSize);

  if(Result==FCE_NOERROR)
  {
    Result=m_Camera.SetParameter(FGP_RESIZE,0);
      
    // Probably packet size is adjusted but this is ok
    if(Result==FCE_ADJUSTED)
     Result=FCE_NOERROR;
  }  
  else
  {
    m_Camera.SetParameter(FGP_RESIZE,0);
  }  
     
  if(Result!=FCE_NOERROR)
   return FALSE;
     
  if(pRect)
  {
    pRect->left=pRect->top=0;
    pRect->right=m_CameraRect.right-m_CameraRect.left;
    pRect->bottom=m_CameraRect.bottom-m_CameraRect.top;
  }
  
  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CameraView reports that selection has changed.

LRESULT CCameraFrame::OnSelection(WPARAM wParam,LPARAM lParam)
{
  RECT r;
  
  // Get selected rectangle
  r.left=LOWORD(wParam); 
  r.right=HIWORD(wParam);
  r.top=LOWORD(lParam);
  r.bottom=HIWORD(lParam);
  
  if(!r.left && !r.top && !r.right && !r.bottom)
  {
    // Undo resize
    DoResize(NULL);
    
    // Set view rectangle to full camera image
    m_ViewRect.left=m_ViewRect.top=0;
    m_ViewRect.right=m_CamParms.XSize;
    m_ViewRect.bottom=m_CamParms.YSize;
    
    // Reset View
    m_pView->Resize();
  }
  else
  {
    // Adjust our view rectangle: View reports offsets within last rectangle
    m_ViewRect.left+=r.left;
    m_ViewRect.top+=r.top;
    m_ViewRect.right=m_ViewRect.left+(r.right-r.left);
    m_ViewRect.bottom=m_ViewRect.top+(r.bottom-r.top);

    // Press control to resize image transmission
    if(0x8000&GetAsyncKeyState(VK_CONTROL))
    {
      // Resize on success, reset view otherwise
      if(DoResize(&m_ViewRect))
       m_pView->Resize(&m_ViewRect,(UINT16)m_ViewRect.right,(UINT16)m_ViewRect.bottom);
      else
       m_pView->Resize(&m_ViewRect);
    }
    else
    {
      m_pView->Resize(&m_ViewRect);
    }  
  }  
    
  return 0;
}

void CCameraFrame::OnOthersLicenseRequest()
{
  char Buffer[32];
  char Msg[256];
  HANDLE Handle;
  char  *pMem;
  
  if(m_Camera.GetLicenseRequest(Buffer,sizeof(Buffer))==FCE_NOERROR)
  {
    // Copy to clipboard
    if(OpenClipboard())
    {
      EmptyClipboard();
      Handle=GlobalAlloc(GHND,strlen(Buffer)+2);
      if(Handle!=NULL)
      {
        pMem=(char*)GlobalLock(Handle);
        strcpy(pMem,Buffer);
        GlobalUnlock(Handle);
        SetClipboardData(CF_TEXT,Handle);
      }
      CloseClipboard();
    }

    wsprintf(Msg,"License Request String: %s\nThis Information has been copied to your clipboard!",
             Buffer);
    MessageBox(Msg);
  }  
}


void CCameraFrame::OnOthersLicenseInfo()
{
  UINT32 Value;
  
  if(m_Camera.GetParameter(FGP_LICENSEINFO,&Value)==FCE_NOERROR)
   MessageBox(InfoStr[Value],"License Type");
  
}

